{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6c382be5-683d-4fff-853c-659c48289d46",
   "metadata": {},
   "source": [
    "## 不同的Memory工具\n",
    "\n",
    "- 短时记忆：存储在内存中\n",
    "- 构建记忆实体清单\n",
    "- 接入知识图谱\n",
    "- 长对话在内存中的处理方式\n",
    "- 长时记忆实现方式"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffd8a448-e262-43a7-a74e-72628751c758",
   "metadata": {},
   "source": [
    "### 内存中的短时记忆"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4ea62e74-b8c9-4b08-a534-f7b1c509feb6",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\Tingwang.Gao\\AppData\\Local\\Temp\\ipykernel_31620\\4077081215.py:4: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/\n",
      "  memory = ConversationBufferMemory()\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'history': 'Human: 你好，我是人类！\\nAI: 你好，我是AI，有什么可以帮助你的吗？'}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.memory import  ConversationBufferMemory\n",
    "\n",
    "memory = ConversationBufferMemory()\n",
    "memory.chat_memory.add_user_message(\"你好，我是人类！\")\n",
    "memory.chat_memory.add_ai_message(\"你好，我是AI，有什么可以帮助你的吗？\")\n",
    "\n",
    "memory.load_memory_variables({})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "91a80960-95cb-4a24-93ce-9345a39e0eac",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\Tingwang.Gao\\AppData\\Local\\Temp\\ipykernel_31620\\4185949352.py:4: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/\n",
      "  memory = ConversationBufferWindowMemory(k=1)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'history': 'Human: 我想吃鸡肉\\nAI: 好的，我帮你找找鸡肉的做法'}"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#实现一个最近的对话窗口，超过窗口条数的对话将被删除\n",
    "from langchain.memory import  ConversationBufferWindowMemory\n",
    "\n",
    "memory = ConversationBufferWindowMemory(k=1) # 只保留一句对话\n",
    "\n",
    "memory.save_context({\"input\":\"你好，我是人类!\"},{\"output\":\"你好，我是AI，有什么可以帮助你的吗？\"})\n",
    "memory.save_context({\"input\":\"我想吃鸡肉\"},{\"output\":\"好的，我帮你找找鸡肉的做法\"})\n",
    "\n",
    "memory.load_memory_variables({})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19ceb4af-aabd-4182-8361-8b4030f5fad2",
   "metadata": {},
   "source": [
    "### 记忆实体清单"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "674424ae-7600-4ccd-b8d8-3919291ac3b4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构建记忆实体概念清单\n",
    "from langchain.llms import  OpenAI\n",
    "from langchain.memory import ConversationEntityMemory\n",
    "\n",
    "llm = OpenAI(\n",
    "    temperature=0\n",
    ")\n",
    "\n",
    "memory = ConversationEntityMemory(llm=llm)\n",
    "_input = {\n",
    "    \"input\":\"胡八一和王胖子雪莉杨经常在一起冒险，合称盗墓铁三角.\"\n",
    "}\n",
    "# memory.load_memory_variables(_input)\n",
    "memory.save_context(\n",
    "    _input,\n",
    "    {\n",
    "        \"output\":\"听起来很刺激，我也想加入他们！\"\n",
    "    }\n",
    ")\n",
    "memory.load_memory_variables({\"input\":\"铁三角是谁?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efe6a32a-3f8a-4ba5-9a4c-b521c0b247ba",
   "metadata": {},
   "source": [
    "### 知识图谱"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b3f40d6-2866-48aa-a92b-914b1388236f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.llms import  OpenAI\n",
    "from langchain.memory import ConversationKGMemory\n",
    "\n",
    "llm = OpenAI(\n",
    "    temperature=0\n",
    ")   \n",
    "\n",
    "memory = ConversationKGMemory(llm=llm,return_messages=True)\n",
    "\n",
    "memory.save_context(\n",
    "    {\"input\":\"帮我找一下tomie\"},\n",
    "    {\"output\":\"对不起请问什么是tomie？\"}\n",
    ")\n",
    "\n",
    "memory.save_context(\n",
    "    {\"input\":\"tomie是一个培训讲师\"},\n",
    "    {\"output\":\"好的，我知道了。\"}\n",
    ")\n",
    "\n",
    "memory.load_memory_variables({\"input\":\"tomie是谁?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9c73f18-6004-4479-b466-5e88ba663a21",
   "metadata": {},
   "source": [
    "## 长对话在内存中的处理方式\n",
    "- 总结摘要，减少存储内容\n",
    "- token计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc412bbb-5cb0-4039-bd1d-9c21f1fdb326",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 总结摘要\n",
    "from langchain.memory import ConversationSummaryMemory\n",
    "from langchain.llms import  OpenAI\n",
    "\n",
    "llm = OpenAI(\n",
    "    temperature=0\n",
    ")\n",
    "\n",
    "memory = ConversationSummaryMemory(llm=llm)\n",
    "memory.save_context(\n",
    "    {\"input\":\"帮我找一下tomie\"},\n",
    "    {\"output\":\"对不起请问什么是tomie？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"tomie是一个培训讲师\"},\n",
    "    {\"output\":\"好的，我知道了。\"}\n",
    ")\n",
    "\n",
    "memory.load_memory_variables({})\n",
    "# 产生摘要\n",
    "memory.predict_new_summary(messages,\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "453edd5c-271b-47d1-8f94-3c13810df4c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "#使用ChatMessageHistory来快速获得对话摘要\n",
    "from langchain.memory import ConversationSummaryMemory\n",
    "from langchain.memory import ChatMessageHistory\n",
    "from langchain.llms import  OpenAI\n",
    "\n",
    "hisiory = ChatMessageHistory()\n",
    "hisiory.add_user_message(\"你好，我是人类！\")\n",
    "hisiory.add_ai_message(\"你好，我是AI小丸子，有什么可以帮助你的吗？\")\n",
    "\n",
    "#memory = ConversationSummaryMemory.from_messages(\n",
    "#    llm=OpenAI(temperature=0),\n",
    "#    chat_memory=hisiory,\n",
    "#    return_messages=True\n",
    "#)\n",
    "# memory.buffer\n",
    "\n",
    "memory = ConversationSummaryMemory(\n",
    "    llm=OpenAI(temperature=0),\n",
    "    return_messages=True,\n",
    "    buffer=\"The AI introduces itself as AI Little Maruko and asks if there is anything it can help the human with.\",\n",
    "    chat_memory=hisiory\n",
    ")\n",
    "memory.buffer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83eefb17-c661-4079-9978-edda7fdf8a6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 当对话持续进行且对话内容很多的时候\n",
    "# 可以使用ConversationSummaryBufferMemory来存储对话摘要\n",
    "# 这是一种非常有用的方式,它会根据token的数量来自动判断是否需要进行摘要\n",
    "# 当token数量超过阈值的时候,会自动进行摘要\n",
    "# 在缓冲区中,会保留最近的k条对话\n",
    "# 比较久的对话会被删除，在删除前会进行摘要\n",
    "\n",
    "from langchain.memory import ConversationSummaryBufferMemory\n",
    "from langchain.llms import OpenAI\n",
    "llm = OpenAI(temperature=0)\n",
    "memory = ConversationSummaryBufferMemory(\n",
    "    llm=llm,\n",
    "    max_token_limit=150\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"帮我找一下tomie\"},\n",
    "    {\"output\":\"对不起请问什么是tomie？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"tomie是一个培训讲师\"},\n",
    "    {\"output\":\"好的，我知道了。\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"今天他要讲一门关于RAG的课程\"},\n",
    "    {\"output\":\"好的，我知道了。需要RAG的资料吗？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"不需要资料了，谢谢\"},\n",
    "    {\"output\":\"好的，那我就不打扰你了。\"}\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84d6a961-aeea-4bea-9478-edef5b22ea4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Conversation Token Buffer使用token长度来决定什么时候刷新内存\n",
    "\n",
    "from langchain.memory import ConversationTokenBufferMemory\n",
    "from langchain.llms import OpenAI\n",
    "llm = OpenAI(temperature=0)\n",
    "memory = ConversationTokenBufferMemory(\n",
    "    llm=llm,\n",
    "    max_token_limit=150\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"帮我找一下tomie\"},\n",
    "    {\"output\":\"对不起请问什么是tomie？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"tomie是一个培训讲师\"},\n",
    "    {\"output\":\"好的，我知道了。\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"今天他要讲一门关于RAG的课程\"},\n",
    "    {\"output\":\"好的，我知道了。需要RAG的资料吗？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"不需要资料了，谢谢\"},\n",
    "    {\"output\":\"好的，那我就不打扰你了。\"}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dd7c5b5-8e49-4e39-a1e3-aa3e102dda9b",
   "metadata": {},
   "source": [
    "## 长时记忆的实现方式\n",
    "通过向量数据库来存储之前的对话内容，有的向量数据库服务还提供自动摘要等，每次对话的时候，都会从向量数据库里查询最相关的文档或历史对话"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "db2f2840-7eeb-4e9e-946c-562aba724f60",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain.memory import ConversationBufferMemory\n",
    "from langchain.vectorstores import FAISS\n",
    "\n",
    "memory = ConversationBufferMemory()\n",
    "memory.save_context(\n",
    "    {\"input\":\"帮我找一下tomie\"},\n",
    "    {\"output\":\"对不起请问什么是tomie？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"tomie是一个培训讲师\"},\n",
    "    {\"output\":\"好的，我知道了。\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"今天他要讲一门关于RAG的课程\"},\n",
    "    {\"output\":\"好的，我知道了。需要RAG的资料吗？\"}\n",
    ")\n",
    "memory.save_context(\n",
    "    {\"input\":\"不需要资料了，谢谢\"},\n",
    "    {\"output\":\"好的，那我就不打扰你了。\"}\n",
    ")\n",
    "\n",
    "vectorstore = FAISS.from_texts(\n",
    "    memory.buffer.split(\"\\n\"),\n",
    "    OpenAIEmbeddings()\n",
    ")\n",
    "FAISS.save_local(vectorstore,\"test_faiss\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f8a7359-05bc-4c1f-af4e-01bd700159bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.vectorstores import FAISS\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain.memory import VectorStoreRetrieverMemory\n",
    "r1 = FAISS.load_local(\"test_faiss\",OpenAIEmbeddings(),allow_dangerous_deserialization=True)\n",
    "r2 = r1.as_retriever(\n",
    "    search_kwargs={\"k\":1}\n",
    ")\n",
    "memory2 = VectorStoreRetrieverMemory(\n",
    "    retriever=r2\n",
    ")\n",
    "memory2.load_memory_variables({\"prompt\":\"tomie是什么职业?\"})"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (langchain)",
   "language": "python",
   "name": "langchain-env"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
